/**
 * @file swei_login.c
 * @brief
 * @author  ()
 * @version 1.0
 * @date 2024-09-13
 *
 * @copyright Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-09-13 <td>1.0     <td>     <td>内容
 * </table>
 */
#include "include/port/swei_log.h"
#include "include/swei_common.h"
#include "include/port/swei_wrapper.h"
#include "service/include/swei_status.h"
#include "include/swei_protocol.h"
#include "include/swei_socket.h"
#include "utils/utils_timer.h"

#include "include/swei_login.h"
#include "include/swei_config.h"
#include "service/include/swei_active.h"
#include "comm/co_heartbeat.h"
#include "lowpower/include/low_power.h"
#include "misc/misc_system.h"
#include "include/swei_location.h"

#define FIRST_NETREG_TIMEOUT        (2*60*1000)             /* 首次注网超时 */
#define FIRST_CONNECT_TIMEOUT       (10*1000)               /* 首次 socket 连接超时 */
#define SLEEP_COUNT_DOWN            (5*60*1000)             /* 设备登录后允许休眠时间 */
#define ACK_RESPONSE_TIMEOUT        (5000)                  /* 消息包等待接收应答时间 */
#define WAIT_SERVER_RESP_ACTIVE_TM  (10*1000)               /* 等待服务器响应查询激活状态应答(0x000D) */
#define DEV_UNCONN_EXPIRED_TIME     (5*60*1000)
#define MAX_LOGIN_REQUEST_CNT       (3)
#define MAX_ACTIVE_REQUEST_CNT      (3)
#define MAX_HBT_REQUEST_CNT         (3)

login_ctx_t login_ctx;
uint32_t dev_unconn_timer = 0;

/* global retry  5s -> 10s -> 30s -> 60s -> 120s -> 300s */
uint16_t retry_interval[] = {
    5, 10, 30, 90, 180, 300
};

#define RETRY_MAX                   (ARRAY_COUNT(retry_interval) - 1)

/* 函数声明 */
bool net_ready_check(void);
bool socket_ready_check(void);
void swei_login_lowpower_countdown(uint32_t ms);

/**
 * @brief 服务器登录超时计时器, 登录超时则允许休眠
 * @return true 
 * @return false 
 */
bool swei_login_timeout(void)
{
    if(dev_unconn_timer == 0) {
        dev_unconn_timer = getms();
    }

    return timeout_ms(dev_unconn_timer, DEV_UNCONN_EXPIRED_TIME);
}

/**
 * @brief 更新计时器
 * @param  step             My Param doc
 * @param  prefix           My Param doc
 */
void update_retry_timer(utils_time_t *timer, uint32_t retry, char *prefix)
{
    uint32_t interval = 0;

    if (retry < RETRY_MAX)
        interval = retry_interval[retry];
    else
        interval = retry_interval[RETRY_MAX];

    log_w("%s; retry %d times, count down %d seconds!\r\n", prefix, retry, interval);
    utils_time_countdown_ms(timer, interval * 1000);
}

/**
 * @brief 定期打印计时信息
 * @param  timer            My Param doc
 */
void show_timer_info(utils_time_t *timer)
{

}

/**
 * @brief 登录初始化
 */
void swei_login_init(void)
{
    memset(&login_ctx, 0, sizeof(login_ctx_t));
}

/// @brief 登录相关数据去初始化
/// @param
void swei_login_deinit(void)
{

}

/**
 * @brief 服务器登录信息重置
 */
static void swei_login_info_reset(void)
{
    login_ctx.step                      = LOGSTEP_WAIT_NETWORK;
    login_ctx.retry                     = 0;
    login_ctx.need_poweron_act_check    = true;
    utils_time_countdown_ms(&login_ctx.timer, FIRST_NETREG_TIMEOUT);
}

/**
 * @brief 服务器登录逻辑
 */
void swei_login_process(void)
{
    static bool     prev_power_on_status    = false;
    static uint8_t  last_step               = LOGSTEP_IDLE;
    bool            power_on_status         = (is_ebike_mcu_off() ? false : true); 

    /* 开机则重置登录状态, 重新获取激活状态 */
    if (power_on_status && (prev_power_on_status != power_on_status)) {
        prev_power_on_status = power_on_status;
        swei_login_info_reset();
        log_e("CO_MCU power on, reconnect the network and log in!\r\n");
    }

    switch (login_ctx.step)
    {
        case LOGSTEP_IDLE:
            swei_login_info_reset();
            break;

        case LOGSTEP_WAIT_NETWORK:                                              /* 网络连接管理 */
            /* 未超时且网络无注册, 则等待 */
            if (!utils_time_is_expired(&login_ctx.timer) && !net_ready_check()) {
                return;
            }

            if (net_ready_check()) {
                login_ctx.step      = LOGSTEP_WAIT_SOCKET;
                login_ctx.retry     = 0;
                utils_time_countdown_ms(&login_ctx.timer, FIRST_CONNECT_TIMEOUT);
            } else {
                login_ctx.retry++;
                update_retry_timer(&login_ctx.timer, login_ctx.retry, "network connect timeout");
            }
            break;

        case LOGSTEP_WAIT_SOCKET:                                               /* 套接字连接管理 */
            /* 未超时且socket未连接, 则等待 */
            if (!utils_time_is_expired(&login_ctx.timer) && !socket_ready_check()) {
                return;
            }

            if (socket_ready_check()) {
                login_ctx.step      = LOGSTEP_LOGIN;
                login_ctx.retry     = 0;
            } else {
                /* socket尝试创建超过最大次数, 则重新注网 */
                if (++login_ctx.retry > RETRY_MAX) {
                    log_e("socket connected timeout; reconnect the network!\r\n");
                    swei_network_reconnect();
                    login_ctx.step  = LOGSTEP_WAIT_NETWORK;
                    login_ctx.retry = 0;
                    utils_time_countdown_ms(&login_ctx.timer, FIRST_NETREG_TIMEOUT);
                    osDelay(1000);                                              /* 等待底层释放资源 */
                }
                update_retry_timer(&login_ctx.timer, login_ctx.retry, "socket timeout");
            }
            break;

        case LOGSTEP_LOGIN:                                                     /* 登录服务器流程 */
            /* 发送登录包 */
            swei_srv_login(login_ctx.retry);
            utils_time_countdown_ms(&login_ctx.timer, ACK_RESPONSE_TIMEOUT);
            login_ctx.step  = LOGSTEP_LOGIN_ACK;
            login_ctx.ack   = false;
            break;

        case LOGSTEP_LOGIN_ACK:
            /* 未超时且设备未登录成功, 则等待 */
            if(!utils_time_is_expired(&login_ctx.timer) && !login_ctx.ack) {
                return;
            }

            /* 设备未登录 */
            if (!login_ctx.ack) {
                if (++login_ctx.retry > MAX_LOGIN_REQUEST_CNT) {
                    /* 超过最大次数, 重新注网 */
                    log_e("login timeout; reconnect the network!\r\n");
                    swei_network_reconnect();
                    login_ctx.step  = LOGSTEP_WAIT_NETWORK;
                    login_ctx.retry = 0;
                    utils_time_countdown_ms(&login_ctx.timer, FIRST_NETREG_TIMEOUT);
                    osDelay(1000);
                    break;
                }
                login_ctx.step      = LOGSTEP_LOGIN;                            /* 重新发送登录包 */
                log_i("login timeout; retry %d times\r\n", login_ctx.retry);
                break;
            }

            /* 设备关机后开机，首次登录才去查询激活状态，否则登录服务器后直接跳过激活查询(问题单修复!!!) */
            if (login_ctx.active_req_finish && !login_ctx.need_poweron_act_check) {
                login_ctx.step                      = LOGSTEP_HBT;
                login_ctx.retry                     = 0;
            } else {
                login_ctx.step                      = LOGSTEP_ACTIVE;
                login_ctx.retry                     = 0;
                login_ctx.need_poweron_act_check    = false;
            }
            /* 每次登录上报设备版本信息 */
            swei_status_event_update(TBOX_UPLOAD_DEV_VERSION, 0);
            login_ctx.login_rdy = true;
            break;

        case LOGSTEP_ACTIVE:                                                    /* 查询激活 */
            /* 发送激活查询 */
            login_ctx.ack                   = false;
            login_ctx.active_req_finish     = false;
            login_ctx.step                  = LOGSTEP_ACTIVE_ACK;
            swei_srv_active();
            utils_time_countdown_ms(&login_ctx.timer, ACK_RESPONSE_TIMEOUT);
            break;

        case LOGSTEP_ACTIVE_ACK:
            /* 未超时且设备未激活, 则等待 */
            if (!utils_time_is_expired(&login_ctx.timer) && !login_ctx.ack) {
                return;
            }

            if (!login_ctx.ack) {                                               /* 设备未鉴权，重复检查5次后跳过鉴权检查 */
                dev_unconn_timer = getms();

                login_ctx.retry++;
                if (login_ctx.retry > MAX_ACTIVE_REQUEST_CNT) {
                    login_ctx.retry = 0;
                    login_ctx.step  = LOGSTEP_HBT;                              /* 超过最大次数, 尝试用本地存储的激活状态 */
                    log_e("active timeout; directly try skip heartbeat step!\r\n");
                    break;
                }
                log_i("active timeout; retry %d times\r\n", login_ctx.retry);
                login_ctx.step      = LOGSTEP_ACTIVE;                           /* 重新发送激活包 */
    
            } else {
                login_ctx.step      = LOGSTEP_ACTIVE_WAIT;
                utils_time_countdown_ms(&login_ctx.timer, WAIT_SERVER_RESP_ACTIVE_TM);
                swei_login_lowpower_countdown(SLEEP_COUNT_DOWN);
            }
            break;

        case LOGSTEP_ACTIVE_WAIT:
            /* 未超时且设备未激活, 则等待 */
            if (!utils_time_is_expired(&login_ctx.timer) && !login_ctx.active_req_finish) {
                return;
            }

            if (login_ctx.active_req_finish) {
                login_ctx.step  = LOGSTEP_HBT;
                login_ctx.retry = 0;
            }
            else {
                /* 响应超时, 本次请求失败, 重试 */
                login_ctx.step  = LOGSTEP_ACTIVE_ACK;
                login_ctx.ack   = false;
            }
            break;

        case LOGSTEP_HBT:                                                       /* 发送心跳 */
            /* 发送心跳包 */
            swei_srv_heartbeat(login_ctx.retry);
            utils_time_countdown_ms(&login_ctx.timer, ACK_RESPONSE_TIMEOUT);
            login_ctx.ack   = false;
            login_ctx.step  = LOGSTEP_HBT_ACK;
            break;

        case LOGSTEP_HBT_ACK:
            /* 未超时, 则等待 */
            if (!utils_time_is_expired(&login_ctx.timer) && !login_ctx.ack) {
                return;
            }

            if (login_ctx.ack) {
                int32_t intval = swei_config_rtp_intval_update(REPORT_HEARTBEAT);

                utils_time_countdown_ms(&login_ctx.timer, intval * 1000);
                login_ctx.retry             = 0;
                login_ctx.heartbeat_rdy     = true;
                login_ctx.step              = LOGSTEP_HBT_WAIT;                 /* 等待下一次心跳包发送 */

                log_i("countdown for next heartbeat, %d ms.\r\n", intval * 1000);
            }
            else {
                if (++login_ctx.retry > MAX_HBT_REQUEST_CNT) {
                    login_ctx.login_rdy         = false;
                    login_ctx.heartbeat_rdy     = false;
                    login_ctx.step              = LOGSTEP_LOGIN;
                    login_ctx.retry             = 0;
                    
                    break;
                }
                else {
                    /* 等待5s后重发心跳包 */
                    login_ctx.step              = LOGSTEP_HBT_WAIT;
                    utils_time_countdown_ms(&login_ctx.timer, ACK_RESPONSE_TIMEOUT);
                }
                log_i("heartbeat request, retry %d times!\r\n", login_ctx.retry);
            }
            break;

        case LOGSTEP_HBT_WAIT:
            /* 未超时, 则等待 */
            if (!utils_time_is_expired(&login_ctx.timer)) {
                return;
            }
            login_ctx.step = LOGSTEP_HBT;                                       /* 跳转心跳包发送阶段 */
            break;

        default:
            break;
    }

    if (last_step != login_ctx.step) {
        log_i("login step change to : %s\r\n", swei_login_step_get_str());
        last_step = login_ctx.step;
    }
}


/// @brief 查询登录状态
/// @param
/// @return true - 登录正常 false - 未登录
bool swei_login_is_ok(void)
{
    return login_ctx.login_rdy ? true : false;
}

/// @brief 设置应答
/// @param  
/// @return 
void swei_srv_login_ack_set(void)
{
    login_ctx.ack = true;
}

/**
 * @brief 激活状态就绪
 */
void swei_srv_active_req_set_finish(void)
{
    login_ctx.active_req_finish = true;
}

/// @brief 查询心跳状态
/// @param
/// @return true - 心跳正常 false - 心跳异常
bool swei_heartbeat_is_ok(void)
{
    return login_ctx.heartbeat_rdy ? true : false;
}

/// @brief 请求激活应答状态
/// @param
/// @return
bool swei_srv_active_req_finish(void)
{
    return login_ctx.active_req_finish;
}

/**
 * @brief 网络就绪判断
 * @return true - 网络已就绪
 * @return false - 网络异常
 */
bool net_ready_check(void)
{
    return swei_network_is_ready();
}

/**
 * @brief socket状态是否正常
 * @return true
 * @return false
 */
bool socket_ready_check(void)
{
    return swei_socket_connect_ok();
}

/**
 * @brief 获取登录后设备是否允许休眠
 * @return true
 * @return false
 */
bool swei_login_lowpower_allow(void)
{
    if(!login_ctx.login_rdy || !login_ctx.active_req_finish) {
        return false;
    }

    return utils_time_is_expired(&login_ctx.allow_sleep_timer);
}

/**
 * @brief 设置登录后设备允许休眠倒计时
 * @param  ms               My Param doc
 */
void swei_login_lowpower_countdown(uint32_t ms)
{
    utils_time_countdown_ms(&login_ctx.allow_sleep_timer, ms);
}

/**
 * @brief
 * @return true
 * @return false
 */
bool swei_login_finish(void)
{
    if (login_ctx.heartbeat_rdy)
        return true;

    return false;
}

/// @brief 心跳发送中判断
/// @param
/// @return
bool swei_login_heartbeat_is_sending(void)
{
    /* 不是心跳包发送阶段, 则认为心跳包发送成功 */
    if (LOGSTEP_HBT > login_ctx.step || LOGSTEP_HBT_WAIT < login_ctx.step)
        return true;

    // heartbeat.ready 标志不满足当前场景
    if(utils_time_is_expired(&login_ctx.timer)) {
        return true;
    }

    return false;
}

/**
 * @brief Get the heartbeat report next send object
 * @return uint32_t
 */
uint32_t get_heartbeat_report_next_send(void)
{
    if (LOGSTEP_HBT > login_ctx.step || LOGSTEP_HBT_WAIT < login_ctx.step)
        return 0xFFFFFFFF;

    return utils_time_left(&login_ctx.timer);
}


/**
 * @brief Get the heartbeat total timecnt object
 * @return uint32_t 
 */
uint32_t get_heartbeat_total_timecnt(void)
{
    uint32_t intval = swei_config_rtp_intval_update(REPORT_HEARTBEAT);

    return intval * 1000;
}


#define STEP_STR(idx, str) [idx] = str
const char *step_str[] = {
    STEP_STR(LOGSTEP_IDLE, "idle."),
    STEP_STR(LOGSTEP_WAIT_NETWORK, "Wait for network registered."),
    STEP_STR(LOGSTEP_WAIT_SOCKET, "Wait for socket connected."),
    STEP_STR(LOGSTEP_LOGIN, "Request login server(0x000F)."),
    STEP_STR(LOGSTEP_LOGIN_ACK, "Wait for login ack(0x0008)."),
    STEP_STR(LOGSTEP_ACTIVE, "Request active status(0x000C)."),
    STEP_STR(LOGSTEP_ACTIVE_ACK, "Wait for active request ack(0x0008)."),
    STEP_STR(LOGSTEP_ACTIVE_WAIT, "Wait for active status(0x000D)."),
    STEP_STR(LOGSTEP_HBT, "Request heartbeat(0x0002)."),
    STEP_STR(LOGSTEP_HBT_ACK, "Wait for heartbeat ack(0x0008)."),
    STEP_STR(LOGSTEP_HBT_WAIT, "Wait for next heartbeat."),
};

/// @brief 获取当前登录流程
/// @param
/// @return
const char *swei_login_step_get_str(void)
{
    return step_str[login_ctx.step];
}
